package com.hznc.wcs.service.bpm.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.vo.Pagination;
import com.diboot.iam.entity.BaseLoginUser;
import com.diboot.iam.entity.IamUser;
import com.diboot.iam.service.IamUserService;
import com.diboot.iam.util.IamSecurityUtils;

import com.hznc.wcs.constans.BpmConstants;
import com.hznc.wcs.enums.*;
import com.hznc.wcs.exception.BusinessException;
import com.hznc.wcs.service.bpm.BpmProcessInstanceCopyService;
import com.hznc.wcs.service.bpm.BpmProcessInstanceService;
import com.hznc.wcs.service.bpm.BpmProcessTaskService;
import com.hznc.wcs.util.BpmnModelUtils;
import com.hznc.wcs.util.DateUtils;
import com.hznc.wcs.util.FlowableUtils;
import com.hznc.wcs.vo.bpm.*;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description : 流程任务业务实现
 * @Date : 2024/6/20
 * @Author : ChenJin
 * @Version : v1.0.0
 **/
@Service
public class BpmProcessTaskServiceImpl implements BpmProcessTaskService {

    @Resource
    private BpmProcessInstanceService bpmProcessInstanceService;
    
    @Resource
    private RepositoryService repositoryService;
    
    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;
    
    @Resource
    private HistoryService historyService;

    @Resource
    private ManagementService managementService;

    @Resource
    private IamUserService iamUserService;
    
    @Resource
    private BpmProcessInstanceCopyService bpmProcessInstanceCopyService;


    @Override
    public List<BpmTaskRespVO> getTodoList(BpmTaskReqVO reqVO, Pagination pagination) {
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(reqVO.getProcessInstanceId())
                .taskAssignee(currentUser.getId())
                .active()
                .includeProcessVariables()
                .orderByTaskCreateTime().desc();
        if (StringUtils.isNotEmpty(reqVO.getName())){
            taskQuery.taskNameLike("%"+reqVO.getName()+"%");
        }
        if (ObjectUtils.isNotEmpty(reqVO.getStartTime())&&ObjectUtils.isNotEmpty(reqVO.getEndTime())){
            taskQuery.taskCreatedAfter(DateUtils.of(reqVO.getStartTime()));
            taskQuery.taskCreatedBefore(DateUtils.of(reqVO.getEndTime()));
        }
        long count = taskQuery.count();
        pagination.setTotalCount(count);
        if(count <= 0){
            return new ArrayList<>();
        }
        //查询任务关联流程实例
        List<Task> taskList = taskQuery.listPage(pagination.getPageIndex() - 1, pagination.getPageSize());
        Set<String> processInstanceIds = taskList.stream().map(Task::getProcessInstanceId).collect(Collectors.toSet());
        List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processInstanceIds(processInstanceIds).list();
        //查询流程实例发起人信息
        Map<String, ProcessInstance> instanceMap = processInstanceList.stream().collect(Collectors.toMap(ProcessInstance::getProcessInstanceId, processInstance -> processInstance));
        List<String> startUserIds = processInstanceList.stream().map(ProcessInstance::getStartUserId).collect(Collectors.toList());
        List<IamUser> startUserList = iamUserService.getEntityListByIds(startUserIds);
        Map<String, IamUser> startUserMap = startUserList.stream().collect(Collectors.toMap(IamUser::getId, iamUser -> iamUser));
        //封装待办返回信息
        return taskList.stream().map(task -> {
            BpmTaskRespVO taskRespVO = BeanUtils.convert(task,BpmTaskRespVO.class);
            ProcessInstance processInstance = instanceMap.get(task.getProcessInstanceId());
            if (processInstance == null)
                return null;
            taskRespVO.setProcessInstance(BeanUtils.convert(processInstance, BpmTaskRespVO.ProcessInstance.class));
            IamUser user = startUserMap.get(processInstance.getStartUserId());
            taskRespVO.getProcessInstance().setStartUser(BeanUtils.convert(user, BpmProcessInstanceRespVO.User.class));
            taskRespVO.setModelKey(processInstance.getProcessDefinitionKey());
            taskRespVO.setBusinessKey(processInstance.getBusinessKey());
            return taskRespVO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<BpmTaskRespVO> getDoneList(BpmTaskReqVO reqVO, Pagination pagination) {
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()
                .finished() // 已完成
                .taskAssignee(String.valueOf(currentUser.getId())) // 分配给自己
                .includeTaskLocalVariables()
                .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
        if (StringUtils.isNotEmpty(reqVO.getName())){
            taskQuery.taskNameLike("%"+reqVO.getName()+"%");
        }
        long count = taskQuery.count();
        if (count<=0){
            return new ArrayList<>();
        }
        List<HistoricTaskInstance> historicTaskInstances = taskQuery.listPage(pagination.getPageIndex() - 1, pagination.getPageSize());
        //查询关联流程实例
        Set<String> historyInstanceIds = historicTaskInstances.stream().map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet());
        List<HistoricProcessInstance> historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceIds(historyInstanceIds).list();
        Map<String, HistoricProcessInstance> historicProcessInstanceMap = historicProcessInstanceList.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, historicProcessInstance -> historicProcessInstance));
        //查询流程发起人信息
        List<String> startUserIds = historicProcessInstanceList.stream().map(HistoricProcessInstance::getStartUserId).collect(Collectors.toList());
        List<IamUser> startUserList = iamUserService.getEntityListByIds(startUserIds);
        Map<String, IamUser> startUserMap = startUserList.stream().collect(Collectors.toMap(IamUser::getId, iamUser -> iamUser));
        return historicTaskInstances.stream().map(historicTask -> {
            BpmTaskRespVO taskRespVO = BeanUtils.convert(historicTask,BpmTaskRespVO.class);
            HistoricProcessInstance historicProcessInstance = historicProcessInstanceMap.get(historicTask.getProcessInstanceId());
            if (historicProcessInstance == null)
                return null;
            taskRespVO.setProcessInstance(BeanUtils.convert(historicProcessInstance, BpmTaskRespVO.ProcessInstance.class));
            IamUser user = startUserMap.get(historicProcessInstance.getStartUserId());
            taskRespVO.getProcessInstance().setStartUser(BeanUtils.convert(user,BpmProcessInstanceRespVO.User.class));
            taskRespVO.setModelKey(historicProcessInstance.getProcessDefinitionKey());
            taskRespVO.setBusinessKey(historicProcessInstance.getBusinessKey());
            return taskRespVO;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveTask(BpmTaskApproveReqVO reqVO) {
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        //校验任务存在
        Task task = validateTaskExist(reqVO.getId());
        //判断当前登录用户是不是当前任务处理人
        if (!Objects.equals(currentUser.getId(), task.getAssignee())) {
            throw new BusinessException("当前登录用户不是当前任务处理人");
        }
        //判断流程实例是否存在
        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(task.getProcessInstanceId());
        if(ObjectUtils.isEmpty(processInstance)){
            throw new BusinessException("流程实例不存在");
        }
        //抄送
        if(!CollectionUtils.isEmpty(reqVO.getCopyUserIds())){
            bpmProcessInstanceCopyService.createBpmProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
        }
        //处理委派任务,即当前任务是被委派的子任务
        if (DelegationState.PENDING.equals(task.getDelegationState())){
            approveDelegateTask(task,reqVO);
            return;
        }
        //处理任务后有加签任务,即当前要处理的任务后有加签子任务
        if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())){
            approveAfterSignTask(task,reqVO);
            return;
        }
        //处理普通任务
        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());
        taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
        if (CollectionUtils.isEmpty(reqVO.getVariables())){
            taskService.complete(task.getId());
        }else {
            Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());
            String approve = (String) variables.get("approve");
            if ("0".equals(approve)){
                runtimeService.setVariable(task.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus());
            }
            taskService.complete(task.getId(),variables);
        }
        //处理加签任务,即当前要处理的任务是加签子任务
        handleParentTaskIfSign(task.getParentTaskId());

    }

    @Override
    @Transactional
    public void rejectTask(BpmTaskRejectReqVO reqVO) {
        Task task = validateTaskExist(reqVO.getId());
        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (ObjectUtils.isEmpty(processInstance)) {
            throw new BusinessException("流程实例不存在");
        }
        //修改任务状态
        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason());
        //添加评论
        taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
                BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));
        //修改流程实例状态
        //更新流程实例 status
        runtimeService.setVariable(task.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus());
        //删除流程实例，以实现驳回任务时，取消整个审批流程
        runtimeService.deleteProcessInstance(processInstance.getId(), StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reqVO.getReason())));
    }

    @Override
    public List<UserTask> getReturnTaskList(String id) {
        // 1.1 校验当前任务 task 存在
        Task task = validateTaskExist(id);
        // 1.2 根据流程定义获取流程模型信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
        if (source == null) {
            throw new BusinessException("任务不存在");
        }
        // 2.1 查询该任务的前置任务节点的 key 集合
        List<UserTask> previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null);
        if (CollUtil.isEmpty(previousUserList)) {
            return Collections.emptyList();
        }
        // 2.2 过滤：只有串行可到达的节点，才可以回退。类似非串行、子流程无法退回
        previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
        return previousUserList;
    }

    @Override
    public void returnTask(BpmTaskReturnReqVO reqVO) {
        Task task = validateTaskExist(reqVO.getId());
        if (task.isSuspended()){
            throw new BusinessException("当前任务已被挂起,不能操作"); 
        }
        FlowElement flowElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId());
        returnTask(task,flowElement,reqVO);
    }

    @Override
    @Transactional
    public void delegateTask(BpmTaskDelegateReqVO reqVO) {
        String taskId = reqVO.getId();
        //校验任务
        Task task = validateTaskExist(taskId);
        if (task.getAssignee().equals(reqVO.getDelegateUserId())){
            //当前处理人和被委派的人相同,委派失败
            throw new BusinessException("当前处理人与被委派人相同,委派失败");
        }
        //校验卑微派人是否存在
        IamUser delegateUser = iamUserService.getEntity(reqVO.getDelegateUserId());
        if (ObjectUtils.isEmpty(delegateUser)){
            throw new BusinessException("被委派人不存在,委派失败");
        }
        //添加委派意见
        taskService.addComment(taskId,task.getProcessInstanceId(),BpmCommentTypeEnum.DELEGATE_START.getType(),BpmCommentTypeEnum.DELEGATE_START.formatComment(reqVO.getReason()));
        //设置own为原任务处理人
        taskService.setOwner(taskId,task.getAssignee());
        //设置处理人为被委派人
        taskService.delegateTask(taskId,reqVO.getDelegateUserId());
        //更新任务状态
        updateTaskStatus(taskId,BpmTaskStatusEnum.DELEGATE.getStatus());
    }

    @Override
    @Transactional
    public void transferTask(BpmTaskTransferReqVO reqVO) {
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        String taskId = reqVO.getId();
        Task task = validateTaskExist(taskId);
        if (task.getAssignee().equals(reqVO.getAssigneeUserId())){
            //当前处理人和被委派的人相同,委派失败
            throw new BusinessException("当前处理人与被转派人相同,转派失败");
        }
        IamUser transferUser = iamUserService.getEntity(reqVO.getAssigneeUserId());
        if (ObjectUtils.isEmpty(transferUser)){
            throw new BusinessException("被转派人不存在,转派失败");
        }
        taskService.addComment(taskId,task.getProcessInstanceId(),BpmCommentTypeEnum.TRANSFER.getType(),BpmCommentTypeEnum.TRANSFER.formatComment(reqVO.getReason()));
        taskService.setOwner(taskId,task.getAssignee());
        taskService.setAssignee(taskId, reqVO.getAssigneeUserId());
    }

    @Override
    @Transactional
    public void addSignTask(BpmTaskAddSignReqVO reqVO) {
        //校验任务
        TaskEntityImpl task = (TaskEntityImpl) validateTaskExist(reqVO.getTaskId());
        //向前加签和向后加签不能同时存在
        //当前任务若有向前加签子任务,此次加签只能是向前加签;当前任务若有向后加签子任务,此次加签只能是向后加签
        if(ObjectUtil.isNotEmpty(task.getScopeType())&&ObjectUtil.equals(task.getScopeType(),reqVO.getSignType())){
            return;
        }
        //同一个key的任务,审批人不能相同
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).taskDefinitionKey(task.getTaskDefinitionKey()).list();
        List<String> assigneeList = taskList.stream().map(Task::getAssignee).collect(Collectors.toList());
        List<String> ownerList = taskList.stream().map(Task::getOwner).collect(Collectors.toList());
        assigneeList.addAll(ownerList);
        if (CollUtil.containsAny(assigneeList,reqVO.getSignUserIds())){
            throw new BusinessException("加签处理人已是任务审批人,加签失败");
        }
        //加签人是否存在
        List<IamUser> signUserList = iamUserService.getEntityListByIds(reqVO.getSignUserIds());
        if(CollUtil.isEmpty(signUserList)){
            throw new BusinessException("加签处理人不存在,加签失败");
        }
        task.setCountEnabled(true);
        //向前加签
        if(BpmTaskSignTypeEnum.BEFORE.getType().equals(reqVO.getSignType())){
            task.setOwner(task.getAssignee());
            task.setAssignee(null);
            updateTaskStatus(task.getId(),BpmTaskStatusEnum.WAIT.getStatus());
        }
        task.setScopeType(reqVO.getSignType());
        taskService.saveTask(task);
        //创建加签子任务
        createSignTaskList(reqVO.getSignUserIds(),task);
        //添加加签评论
        taskService.addComment(reqVO.getTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType(), reqVO.getReason());
        
    }

    @Override
    @Transactional
    public void deleteSignTask(BpmTaskAddSignReqVO reqVO) {
        //检验任务
        Task task = validateTaskExist(reqVO.getTaskId());
        if (task.getParentTaskId() == null) {
            throw new BusinessException("父任务不存在");
        }
        Task parentTask = getTask(task.getParentTaskId());
        if (parentTask == null) {
            throw new BusinessException("父任务不存在");
        }
        if (BpmTaskSignTypeEnum.of(parentTask.getScopeType()) == null) {
            throw new BusinessException("父任务不存在");
        }
        //获取子任务列表
        List<Task> subTaskList = getAllChildTaskList(task);
        subTaskList.add(task);
        subTaskList.forEach(subTask->{
            updateTaskStatusAndReason(subTask.getId(),BpmTaskStatusEnum.CANCEL.getStatus(), reqVO.getReason());
        });
        taskService.deleteTasks(subTaskList.stream().map(Task::getId).collect(Collectors.toList()));
        taskService.addComment(task.getParentTaskId(),task.getProcessInstanceId(),BpmCommentTypeEnum.SUB_SIGN.getType(), reqVO.getReason());
        handleParentTaskIfSign(task.getParentTaskId());
    }

    @Override
    public List<BpmTaskRespVO> getDeleteSignTaskList(String parentTaskId) {
        List<Task> taskListByParentTaskId = getTaskListByParentTaskId(parentTaskId);
        List<String> assigneeList = taskListByParentTaskId.stream().map(Task::getAssignee).distinct().collect(Collectors.toList());
        List<String> ownerList = taskListByParentTaskId.stream().map(Task::getOwner).distinct().collect(Collectors.toList());
        List<IamUser> assigneeUserList = iamUserService.getEntityListByIds(assigneeList);
        Map<String, IamUser> assigneeMap = assigneeUserList.stream().collect(Collectors.toMap(IamUser::getId, user -> user));
        List<IamUser> ownerUserList = iamUserService.getEntityListByIds(ownerList);
        Map<String, IamUser> ownerMap = ownerUserList.stream().collect(Collectors.toMap(IamUser::getId, user -> user));
        return taskListByParentTaskId.stream().map(task -> {
            BpmTaskRespVO bpmTaskRespVO = BeanUtils.convert(task,BpmTaskRespVO.class);
            bpmTaskRespVO.setAssigneeUser(BeanUtils.convert(assigneeMap.get(task.getAssignee()),BpmProcessInstanceRespVO.User.class));
            bpmTaskRespVO.setOwnerUser(BeanUtils.convert(ownerMap.get(task.getOwner()),BpmProcessInstanceRespVO.User.class));
            return bpmTaskRespVO;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void commitUserTask(String processInstanceId) {
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).taskAssignee(currentUser.getId())
                .active().singleResult();
        if (ObjectUtils.isEmpty(task)){
            throw new BusinessException("无任务执行");
        }
        BpmTaskApproveReqVO bpmTaskApproveReqVO = new BpmTaskApproveReqVO();
        bpmTaskApproveReqVO.setId(task.getId());
        bpmTaskApproveReqVO.setReason("自动审批");
        this.approveTask(bpmTaskApproveReqVO);
    }

    private Task getTask(String id) {
        return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult();
    }
    
    private List<Task> getTaskListByParentTaskId(String parentTaskId) {
        String tableName = managementService.getTableName(TaskEntity.class);
        // taskService.createTaskQuery() 没有 parentId 参数，所以写 sql 查询
        String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";
        return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list();
    }

    /**
     * 获得所有子任务列表
     *
     * @param parentTask 父任务
     * @return 所有子任务列表
     */
    private List<Task> getAllChildTaskList(Task parentTask) {
        List<Task> result = new ArrayList<>();
        // 1. 递归获取子级
        Stack<Task> stack = new Stack<>();
        stack.push(parentTask);
        // 2. 递归遍历
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            if (stack.isEmpty()) {
                break;
            }
            // 2.1 获取子任务们
            Task task = stack.pop();
            List<Task> childTaskList = getTaskListByParentTaskId(task.getId());
            // 2.2 如果非空，则添加到 stack 进一步递归
            if (CollUtil.isNotEmpty(childTaskList)) {
                stack.addAll(childTaskList);
                result.addAll(childTaskList);
            }
        }
        return result;
    }

    /**
     * 创建加签子任务
     *
     * @param userIds 被加签的用户 ID
     * @param taskEntity        被加签的任务
     */
    private void createSignTaskList(List<String> userIds, TaskEntityImpl taskEntity) {
        if (CollUtil.isEmpty(userIds)) {
            return;
        }
        // 创建加签人的新任务，全部基于 taskEntity 为父任务来创建
        for (String addSignId : userIds) {
            if (StrUtil.isBlank(addSignId)) {
                continue;
            }
            createSignTask(taskEntity, addSignId);
        }
    }

    /**
     * 创建加签子任务
     *
     * @param parentTask 父任务
     * @param assignee   子任务的执行人
     */
    private void createSignTask(TaskEntityImpl parentTask, String assignee) {
        //生成子任务
        TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID());
        task.setName(parentTask.getName());
        task.setDescription(parentTask.getDescription());
        task.setCategory(parentTask.getCategory());
        task.setParentTaskId(parentTask.getId());
        task.setProcessDefinitionId(parentTask.getProcessDefinitionId());
        task.setProcessInstanceId(parentTask.getProcessInstanceId());
//        task.setExecutionId(parentTask.getExecutionId());
        task.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
        task.setTaskDefinitionId(parentTask.getTaskDefinitionId());
        task.setPriority(parentTask.getPriority());
        task.setCreateTime(new Date());
        task.setTenantId(parentTask.getTenantId());

        //向前加签，设置审批人
        if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) {
            task.setAssignee(assignee);
            //向后加签，设置 owner 不设置 assignee 是因为不能同时审批，需要等父任务完成
        } else {
            task.setOwner(assignee);
        }
        //保存子任务
        taskService.saveTask(task);

        //向后前签，设置子任务的状态为 WAIT，因为需要等父任务审批完
        if (BpmTaskSignTypeEnum.AFTER.getType().equals(parentTask.getScopeType())) {
            updateTaskStatus(task.getId(), BpmTaskStatusEnum.WAIT.getStatus());
        }
    }

    /**
     * 判断任务是否存在
     * @param id
     * @return
     */
    private Task validateTaskExist(String id) {
        Task task = getTask(id);
        if (task == null) {
            throw new BusinessException("任务不存在");
        }
        return task;
    }

    /**
     * 更新流程任务的 status 状态
     * @param id    任务编号
     * @param status 状态
     */
    private void updateTaskStatus(String id, Integer status) {
        taskService.setVariableLocal(id, BpmConstants.TASK_VARIABLE_STATUS, status);
    }

    /**
     * 更新流程任务的 status 状态、reason 理由
     *
     * @param id 任务编号
     * @param status 状态
     * @param reason 理由（审批通过、审批不通过的理由）
     */
    private void updateTaskStatusAndReason(String id, Integer status, String reason) {
        updateTaskStatus(id, status);
        taskService.setVariableLocal(id, BpmConstants.TASK_VARIABLE_REASON, reason);
    }

    /**
     * 处理委派任务
     * @param task
     * @param reqVO
     */
    private void approveDelegateTask(Task task,BpmTaskApproveReqVO reqVO) {
        //当前登录用户,也就是被委托的用户
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        //发起委托的用户
        IamUser ownerUser = iamUserService.getEntity(task.getOwner());
        Assert.notNull(ownerUser, "委派任务找不到原审批人，需要检查数据");
        //添加审批意见
        taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END.getType(),
                BpmCommentTypeEnum.DELEGATE_END.formatComment(currentUser.getDisplayName(), ownerUser.getDisplayName(), reqVO.getReason()));

        //调用 resolveTask 完成任务。
        //底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner())：将 owner 设置为 assignee
        taskService.resolveTask(task.getId());
        //更新 task 状态 + 原因
        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus(), reqVO.getReason());
    }

    /**
     * 要处理的任务后有加签任务
     * @param task
     * @param reqVO
     */
    private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) {
        // 更新父 task 状态 + 原因
        updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVING.getStatus(), reqVO.getReason());

        // 2. 激活子任务
        List<Task> childrenTaskList = getTaskListByParentTaskId(task.getId());
        for (Task childrenTask : childrenTaskList) {
            taskService.resolveTask(childrenTask.getId());
            // 更新子 task 状态
            updateTaskStatus(childrenTask.getId(), BpmTaskStatusEnum.RUNNING.getStatus());
        }
    }

    /**
     * 如果父任务是有前后【加签】的任务，如果它【加签】出来的子任务都被处理，需要处理父任务：
     *
     * 1. 如果是【向前】加签，则需要重新激活父任务，让它可以被审批
     * 2. 如果是【向后】加签，则需要完成父任务，让它完成审批
     *
     * @param parentTaskId 父任务编号
     */
    private void handleParentTaskIfSign(String parentTaskId) {
        if (StrUtil.isBlank(parentTaskId)) {
            return;
        }
        // 1.1 判断是否还有子任务。如果没有，就不处理
        Long childrenTaskCount = getTaskCountByParentTaskId(parentTaskId);
        if (childrenTaskCount > 0) {
            return;
        }
        // 1.2 只处理加签的父任务
        Task parentTask = validateTaskExist(parentTaskId);
        String scopeType = parentTask.getScopeType();
        if (BpmTaskSignTypeEnum.of(scopeType) == null) {
            return;
        }

        // 2. 子任务已处理完成，清空 scopeType 字段，修改 parentTask 信息，方便后续可以继续向前后向后加签
        TaskEntityImpl parentTaskImpl = (TaskEntityImpl) parentTask;
        parentTaskImpl.setScopeType(null);
        taskService.saveTask(parentTaskImpl);

        // 3.1 情况一：处理向【向前】加签
        if (BpmTaskSignTypeEnum.BEFORE.getType().equals(scopeType)) {
            // 3.1.1 owner 重新赋值给父任务的 assignee，这样它就可以被审批
            taskService.resolveTask(parentTaskId);
            // 3.1.2 更新流程任务 status
            updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus());
            // 3.2 情况二：处理向【向后】加签
        } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) {
            // 只有 parentTask 处于 APPROVING 的情况下，才可以继续 complete 完成
            // 否则，一个未审批的 parentTask 任务，在加签出来的任务都被减签的情况下，就直接完成审批，这样会存在问题
            Integer status = (Integer) parentTask.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
            if (ObjectUtil.notEqual(status, BpmTaskStatusEnum.APPROVING.getStatus())) {
                return;
            }
            // 3.2.2 完成自己（因为它已经没有子任务，所以也可以完成）
            updateTaskStatus(parentTaskId, BpmTaskStatusEnum.APPROVE.getStatus());
            taskService.complete(parentTaskId);
        }

        // 4. 递归处理父任务
        handleParentTaskIfSign(parentTask.getParentTaskId());

    }

    private Long getTaskCountByParentTaskId(String parentTaskId) {
        String tableName = managementService.getTableName(TaskEntity.class);
        String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}";
        return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count();
    }

    /**
     * 回退流程节点时，校验目标任务节点是否可回退
     *
     * @param sourceKey           当前任务节点 Key
     * @param targetKey           目标任务节点 key
     * @param processDefinitionId 当前流程定义 ID
     * @return 目标任务节点元素
     */
    private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) {
        // 1.1 获取流程模型信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        // 1.3 获取当前任务节点元素
        FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey);
        // 1.3 获取跳转的节点元素
        FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey);
        if (target == null) {
            throw new BusinessException("任务节点不存在");
        }

        // 2.2 只有串行可到达的节点，才可以回退。类似非串行、子流程无法退回
        if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {
            throw new BusinessException("所选节点不可退回");
        }
        return target;
    }
    
    /**
     * 执行回退逻辑
     *
     * @param currentTask   当前回退的任务
     * @param targetElement 需要回退到的目标任务
     * @param reqVO         前端参数封装
     */
    public void returnTask(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
        // 1. 获得所有需要回撤的任务 taskDefinitionKey，用于稍后的 moveActivityIdsToSingleActivityId 回撤
        // 1.1 获取所有正常进行的任务节点 Key
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();
        List<String> runTaskKeyList = taskList.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        // 1.2 通过 targetElement 的出口连线，计算在 runTaskKeyList 有哪些 key 需要被撤回
        // 为什么不直接使用 runTaskKeyList 呢？因为可能存在多个审批分支，例如说：A -> B -> C 和 D -> F，而只要 C 撤回到 A，需要排除掉 F
        List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
        List<String> returnTaskKeyList = returnUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList());

        // 2. 给当前要被回退的 task 数组，设置回退意见
        taskList.forEach(task -> {
            // 需要排除掉，不需要设置回退意见的任务
            if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
                return;
            }
            // 2.1 添加评论
            taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(),
                    BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason()));
            // 2.2 更新 task 状态 + 原因
            updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason());
        });

        // 3. 执行驳回
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(currentTask.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多)
                        reqVO.getTargetTaskDefinitionKey()) // targetKey 跳转到的节点(1)
                .changeState();
    }

}
